import { Command } from 'commander';
import chalk from 'chalk';
import process from 'node:process';
import type { CommandContext, CLIConfig, Logger } from '../types/index.js';
import { CommandRegistry } from './command.js';
import { PluginManager } from './plugin.js';
import { createLogger } from '../utils/logger.js';
import { createConfigManager } from '../utils/config.js';

export class CLIEngine {
  private program: Command;
  private commandRegistry: CommandRegistry;
  private pluginManager: PluginManager;
  private config: CLIConfig;
  private logger: Logger;

  constructor() {
    this.program = new Command();
    this.commandRegistry = new CommandRegistry();
    
    // Initialize config and logger
    const configManager = createConfigManager();
    this.config = configManager.getConfig();
    this.logger = createLogger(this.config.logLevel);
    
    // Initialize plugin manager
    this.pluginManager = new PluginManager(this.commandRegistry, this.config, this.logger);
    
    this.setupProgram();
  }

  private setupProgram(): void {
    this.program
      .name('genwavejs')
      .description('A modern, extensible CLI toolkit framework')
      .version('1.0.0')
      .option('-v, --verbose', 'Enable verbose logging')
      .option('-q, --quiet', 'Suppress non-error output')
      .option('--config <path>', 'Path to config file')
      .hook('preAction', (thisCommand) => {
        const options = thisCommand.opts();
        
        // Adjust log level based on options
        if (options.verbose) {
          this.logger.setLogLevel('debug');
        } else if (options.quiet) {
          this.logger.setLogLevel('error');
        }
      });

    // Add help command
    this.program
      .command('help [command]')
      .description('Display help for command')
      .action((commandName) => {
        if (commandName) {
          const command = this.commandRegistry.get(commandName);
          if (command) {
            console.log(this.getCommandHelp(command));
          } else {
            console.log(chalk.red(`Unknown command: ${commandName}`));
            const similar = this.commandRegistry.findSimilar(commandName);
            if (similar.length > 0) {
              console.log(chalk.yellow(`Did you mean: ${similar.join(', ')}?`));
            }
          }
        } else {
          this.program.help();
        }
      });

    // Add version command
    this.program
      .command('version')
      .description('Display version information')
      .action(() => {
        console.log(`genwavejs v1.0.0`);
        console.log(`Node.js ${process.version}`);
      });
  }

  async initialize(): Promise<void> {
    try {
      // Load plugins from config
      await this.pluginManager.loadPluginsFromConfig();
      
      // Register dynamic commands
      this.registerDynamicCommands();
      
      this.logger.debug('CLI engine initialized successfully');
    } catch (error) {
      this.logger.error('Failed to initialize CLI engine:', error);
      throw error;
    }
  }

  private registerDynamicCommands(): void {
    const commands = this.commandRegistry.list();
    
    for (const command of commands) {
      const cmd = this.program
        .command(command.name)
        .description(command.description);

      // Add aliases
      if (command.aliases) {
        for (const alias of command.aliases) {
          cmd.alias(alias);
        }
      }

      // Add arguments
      if (command.arguments) {
        for (const arg of command.arguments) {
          if (arg.required) {
            cmd.argument(`<${arg.name}>`, arg.description);
          } else {
            cmd.argument(`[${arg.name}]`, arg.description);
          }
        }
      }

      // Add options
      if (command.options) {
        for (const option of command.options) {
          const flag = `--${option.name}`;
          const description = option.description;
          
          if (option.type === 'boolean') {
            cmd.option(flag, description, option.default);
          } else {
            cmd.option(`${flag} <value>`, description, option.default);
          }
        }
      }

      // Set action
      cmd.action(async (...args) => {
        try {
          const options = args[args.length - 1].opts();
          const commandArgs = args.slice(0, -1);
          
          const context: CommandContext = {
            args: commandArgs,
            options,
            cwd: process.cwd(),
            config: this.config,
            logger: this.logger,
          };

          await command.execute(context);
        } catch (error) {
          this.logger.error(`Command failed: ${command.name}`, error);
          process.exit(1);
        }
      });
    }
  }

  private getCommandHelp(command: any): string {
    if (typeof command.getHelp === 'function') {
      return command.getHelp();
    }
    
    let help = `${command.description}\n\n`;
    help += `Usage: genwavejs ${command.name}`;
    
    if (command.arguments) {
      for (const arg of command.arguments) {
        if (arg.required) {
          help += ` <${arg.name}>`;
        } else {
          help += ` [${arg.name}]`;
        }
      }
    }
    
    if (command.options && command.options.length > 0) {
      help += ' [options]';
    }
    
    return help;
  }

  async run(argv?: string[]): Promise<void> {
    try {
      await this.initialize();
      
      // Parse arguments
      if (argv) {
        await this.program.parseAsync(argv);
      } else {
        await this.program.parseAsync();
      }
    } catch (error) {
      this.logger.error('CLI execution failed:', error);
      process.exit(1);
    }
  }

  getCommandRegistry(): CommandRegistry {
    return this.commandRegistry;
  }

  getPluginManager(): PluginManager {
    return this.pluginManager;
  }

  getConfig(): CLIConfig {
    return this.config;
  }

  getLogger(): Logger {
    return this.logger;
  }
}

export const createCLI = (): CLIEngine => {
  return new CLIEngine();
};
